/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jmodcog2.fwd;

import jmodcog2.base.BaseRunner;
import jmodcog2.fwd.config.NetConfig;

/**
 *
 * @author Mitch
 */
public abstract class Net extends BaseRunner {

    protected final boolean bipolar;
    //
    protected final byte num_layers;
    @Constant
    protected final int[] num_nodes;
    @Constant
    protected final int[] num_conns;
    //
    @Constant
    protected final int[] nodes_index0;
    protected final float[] nodes;
    //
    protected final boolean shared_conn;
    @Constant
    protected final int[] conns_index0;
    //protected final float[] conns;
    //
    @Constant
    protected final int[] conns_src_index0;
    @Constant
    protected final int[] conn_src;
    //

    public Net(NetConfig config) {
        super(config);
        this.bipolar = config.isBipolar();
        this.num_layers = config.numLayers();
        this.num_nodes = config.createNumNodes();
        this.num_conns = config.createNumConns();
        this.nodes_index0 = config.createNodesIndexZero();
        this.nodes = new float[config.totalNumNodes()];
        this.shared_conn = config.hasSharedConns();
        this.conns_index0 = config.createConnsIndexZero();
        this.conns_src_index0 = config.createConnsSrcIndexZero();
        this.conn_src = config.createConnsSrc();
    }
    

    protected int _conn_src(int conn_n, byte layer_j, int node_j) {
        if (shared_conn) {
            return conn_src[conns_src_index0[layer_j - 1]
                    + node_j * num_conns[layer_j - 1] + conn_n];
        }
        return conn_src[conns_src_index0[layer_j - 1] + conn_n];
    }

    protected int _node_index(int layer, int node) {
        return nodes_index0[layer] + node;
    }

//    protected abstract boolean _connected(int layer_i, int node_i, int node_j);

    protected abstract int _conn_index(int layer_i, int node_i, int node_j);

    protected float _get_node(int index) {
        if(index < 0) {
            return 0;
        }
        return nodes[index];
    }

    protected void _set_node(int index, float val) {
        if(index >= 0) {
            nodes[index] = val;
        }
    }

    protected abstract float _get_conn_weight(int index);

    protected abstract void _set_conn_weight(int index, float val);
    protected abstract void _add_conn_weight(int index, float val);


    protected float _f(float x) {
        if (bipolar) {
            return -1f + 2f / (1 + exp(x));
        }
        return 1f / (1 + exp(x));
    }

    protected float _df(float _fx) {
        if (bipolar) {
            return 0.5f * (1f + _fx) * (1f - _fx);
        }
        return _fx * (1f - _fx);
    }

    protected void _cpy_in(int n) {
        if(n < input_size) {
            nodes[n] = input[n];
        }
    }

    protected void _cpy_out(int n) {
        if(n < output_size) {
            output[n] = nodes[nodes_index0[num_layers - 1] + n];
        }
    }

    protected void copyIn() {
        setMode(CPY_IN);
        execute(input_size);
    }

    protected void copyOut() {
        setMode(CPY_OUT);
        execute(output_size);
    }


    protected final void setLayer(byte i) {
        LAYER[0] = i;
        put(LAYER);
    }

    protected final boolean setInput(float[] data) {
        if (data.length == input_size) {
            System.arraycopy(data, 0, input, 0, input_size);
            put(input);
            return true;
        }
        return false;
    }

    private boolean dirty_out = false;
    @Override
    public float[] getOutput() {
        if(dirty_out) {
            get(output);
        }
        return output;
    }

    protected final byte[] LAYER = new byte[]{0};
    static final int CPY_IN = 0x0000;
    static final int CPY_OUT = 0x0001;

    @Override
    protected void _run(int mode) {
        if (mode == CPY_IN) {
            int n = getGlobalId(0);
            if(n < input_size) {
                _cpy_in(getGlobalId(0));
            }
        }

        if (mode == CPY_OUT) {
            int n = getGlobalId(0);
            if(n < output_size) {
                _cpy_out(getGlobalId(0));
            }
        }
    }
}
